#include <sys/socket.h>
#include <sys/un.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>

#define PORT 8080                       // 服务器监听端口

int main(){
	/*
	监听  int socket(int domain, int type, int protocol);
	domain：
	域，可传的值为AF_UNIX、AF_LOCAL、AF_INET，AF意为Adress Family。前两个为本机操作，最后一个为IPv4的网络操作，所以为AF_INET
	
	type：
	类型，可传值为SOCK_STREAM、SOCK_DGRAM、SOCK_PACKET等
	SOCK_STREAM 使用 TCP 协议传输数据，SOCK_DGRAM 使用 UDP 协议传输数据，我们要做的是Web服务器，肯定是选择面向连接的可靠的TCP协议，所以这个值传SOCK_STREAM

	protocol:
	所用的协议，有IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP，传0为自动选择协议，所以我们传0

	返回值：
	返回一个socket描述符（socket descriptor），它唯一标识一个socket，这个socket描述字跟文件描述字一样。

	*/
    printf("calling socket()  \n\n");
    int server_socket = socket(AF_INET, SOCK_STREAM, 0);
    
	
	/*
	监听	int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
	
	sockfd:
	socket文件描述符，socket()函数的返回值，也就是server_socket

	addr:
	指向地址结构体的指针，这是一个struct sockaddr类型的通用指针，我们实际创建的结构体为
	struct sockaddr_in {
		__uint8_t   sin_len;
		sa_family_t sin_family;
		in_port_t   sin_port;
		struct  in_addr sin_addr;
		char        sin_zero[8];
	};
	
	addrlen:
	结构体的长度，由于在函数内部无法获取到结构体长度（因为传递的是指针，参考数组），所以需要把长度传入

	返回值:
	绑定成功或者失败的消息码，暂时不作处理

	*/
	printf("creating socketaddr  \n\n");
    struct sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    server_addr.sin_port = htons(PORT);	    
	printf("calling bind()  \n\n");
    bind(server_socket, (struct sockaddr *)&server_addr, sizeof(server_addr));
	
	
    /*
	监听  int listen(int sockfd, int backlog);
	sockfd:
	socket文件描述符，socket()函数的返回值，也就是server_socket

	backlog:
	socket待连接队列的最大个数，一般为5

	返回值:
	绑定成功或者失败的消息码，暂时不作处理
	*/
	printf("calling listen()  \n\n");
    listen(server_socket, 5);
    
	/*
	与客户端建立连接   int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
	
	sockfd:
	socket文件描述符，socket()函数的返回值，也就是server_socket

	addr:
	客户端地址信息的结构体，不关心可以传NULL

	addrlen:
	客户端地址长度，不关心可以传NULL

	返回值:
	socket文件描述符，在与客户端建立连接后，accpet还是会生成一个专门用于和当前客户端通信的socket，而原来那个socket照常负责和其他等待建立连接的客户端建立通信
	*/
	printf("calling accept()  \n\n");
    int client_socket = accept(server_socket, NULL, NULL);
    
	
	/*
	从浏览器读取请求内容  ssize_t read(int fd, void *buf, size_t count);
	
	fd:
	文件描述符，从哪个文件读
	buf：
	读的内容存到buf中
	count:
	共读多少个字节
	*/
	printf("calling read()  \n\n");
    char buf[1024];
    read(client_socket, buf, 1024);
    
    printf("%s",buf);

    char status[] = "HTTP/1.0 200 OK\r\n";
    char header[] = "Server: DWBServer\r\nContent-Type: text/html;charset=utf-8\r\n\r\n";
    char body[] = "<html><head><title>C语言构建小型Web服务器</title></head><body><h2>欢迎</h2><p>Hello，World</p></body></html>";


	/*
	往浏览器写响应内容 	int socket(int domain, int type, int protocol);	
	
	fd:
	文件描述符，往哪个文件写
	buf：
	内容的首地址
	count:
	共读多少个字节	
	*/
	printf("calling write()  \n\n");
    write(client_socket, status, sizeof(status));
    write(client_socket, header, sizeof(header));
    write(client_socket, body, sizeof(body));

	printf("calling close()  \n\n");
    close(client_socket);
    close(server_socket);

    return 0;
}
